<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <title>DJI MINI 2</title>
    <link type="text/css" rel="stylesheet" href="./css/main.css">
    <link type="text/css" rel="stylesheet" href="./css/dock.css">
    <link type="text/css" rel="stylesheet" href="./css/text-up.css">
    <link type="text/css" rel="stylesheet" href="./css/mask.css">
</head>
<body>
<section>
    <div class="header-title display-none">
        <h1 class="title-h1">DJI MINI 2</h1>
        <p class="title-p">快意飞行</p>
    </div>
    <!-- loading相关 -->
    <div class="loading-con">
        <div class="loading">
            <div class="loading-circle"></div>
            <div class="progress"></div>
        </div>
        <div class="loading-complete display-none">
            <p>
                加载完成
                <svg t="1677233206130" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2675" width="24" height="24"><path d="M537.6 102.4c73.5744 0 142.336 20.6848 200.704 56.576 15.872 9.6256 16.7936 23.1424 2.9696 40.5504-14.08 16.2816-43.008 13.2608-86.8352-9.216l-0.0512 0.0512a318.1568 318.1568 0 1 0 104.8064 524.3904h0.3584c19.456-26.7776 40.5504-40.192 63.1296-40.2432l6.8096 0.4096c24.064 4.4032 30.4128 16.6912 19.0464 36.864l0.2048-0.3584-0.1536 0.1536-0.0512 0.2048h-0.1024l-7.936 10.5984A384 384 0 1 1 537.6 102.4z m332.288 201.0112a33.28 33.28 0 0 1 0 47.104l-276.48 276.48a31.2832 31.2832 0 0 1-0.512 0.4608l-3.584 3.584a30.72 30.72 0 0 1-43.4176 0l-1.8432-1.792-0.4096-0.512-135.3216-135.2704a30.72 30.72 0 0 1 0-43.4176l3.584-3.584a30.72 30.72 0 0 1 43.4688 0l112.2304 112.128 255.232-255.1808a33.28 33.28 0 0 1 47.104 0z" fill="#ffffff" p-id="2676"></path></svg>
            </p>
        </div>
    </div>
    <canvas id="canvas"></canvas>

    <!-- mac-dock-->
    <div class="dock_con display-none">
        <div class="dock">
            <div class="dock-item">
                <i class="icon icon_fly" data-name="起飞"></i>
            </div>
            <div class="dock-item">
                <i class="icon icon_color" data-name="颜色贴图"></i>
            </div>
            <div class="dock-item">
                <i class="icon icon_check" data-name="模型切换"></i>
            </div>
            <div class="dock-item">
                <i class="icon icon_photo" data-name="照相"></i>
            </div>
            <div class="dock-item">
                <i class="icon icon_setup" data-name="设置"></i>
            </div>
        </div>
    </div>

   <!-- 颜色贴图-->
    <div class="color-map"></div>

    <!-- 跳动文字-->
    <div class="text-up"></div>

    <!-- 截图-->
    <div class="photo-mask display-none">
        <div class="mask-con">
            <div class="mask-image">
                <img src="" alt="photo-mask" class="img"/>
            </div>
            <div class="mask-text">长按保存</div>

        </div>

        <div class="close-mask">
            <i class="icon icon-close"></i>
        </div>
    </div>

</section>

</body>
<script src="./utils/index.js"></script>
<script src="./js/dock.js"></script>
<script src="./js/textUp.js"></script>
<script type="importmap">
    {
        "imports": {
                "three": "./js/three.module.js"
        }
    }
</script>
<script type="module">
    import * as THREE from 'three';
    import { OrbitControls }  from './js/OrbitControls.js'
    import { GUI } from './js/lil-gui.module.min.js'
    import { GLTFLoader } from './js/loaders/GLTFLoader.js';
    import TWEEN from './js/tween.module.js';
    import { EffectComposer } from './js/postprocessing/EffectComposer.js'
    import { RenderPass } from './js/postprocessing/RenderPass.js'
    import { OutlinePass } from './js/postprocessing/OutlinePass.js'
    import {OutputPass} from './js/postprocessing/OutputPass.js'
    import { ShaderPass } from './js/postprocessing/ShaderPass.js'
    import {GammaCorrectionShader} from './js/shaders/GammaCorrectionShader.js'
    import {FXAAShader} from './js/shaders/FXAAShader.js'
    import JoyStick, { StickStatus } from './libs/JoyStick.js'

    let scene, camera, cubeCamera,cubeRenderTarget,renderer, mesh, controls,
        dLight, raycaster, mixer, tween,grid, grid_1, sprite;
    let gltf,gltf_v11;
    let client = {x:0, y:0}
    let tip = document.createElement('div')

    let mouse = new THREE.Vector2();
    let velocity = new THREE.Vector3();
    let up_vector = new THREE.Vector3(0, 1, 0);
    let temp_vector = new THREE.Vector3();
    let targetPosition = new THREE.Vector3(1.8, 1.9, 3.4);
    let endPosition = new THREE.Vector3(10, 10, 10);
    let axesHelper = new THREE.AxesHelper( 3 );

    let INTERSECTED;
    let selectedObjects = [];
    let timer;
    let longPressTimer;

    let isLoading = true;
    let isJumping = false;
    let isDown = false;
    let isAppendColor = false; // 是否添加了颜色贴图
    let isCheck = false; // 是否切换到飞行模式
    let isFly = false; // 是否准备就绪
    let beginFly = false; // 是否开始飞行

    let jumpVelocity = 0;
    let flySeep = 0; // 飞行速度
    let keyCode = ''; // 获取按键
    let shaders = []; // 添加着色

    let composer, outlinePass, effectFXAA; // 后期处理相关

    const canvas = document.querySelector('#canvas');
    const dock = document.querySelector('.dock');
    const dock_con = document.querySelector('.dock_con');
    const progress = document.querySelector(".progress");
    const loading_complete = document.querySelector('.loading-complete');
    const loading_con = document.querySelector('.loading-con');
    const title = document.querySelector(".header-title");
    const color_map = document.querySelector(".color-map");
    const closeMask = document.querySelector('.close-mask');
    const photoMask = document.querySelector('.photo-mask');
    const imageMask = document.querySelector('.mask-image img');

    const dampingFactor = 0.05;
    const maxJumpHeight = 2;
    const colorMap = ["#ffffff","#9287E7", "#FF7B7B", "#FEB64D", "#49D6A5", "#32D3EB"];

    canvas.height = window.innerHeight;
    canvas.width = window.innerWidth;

    const throttledScrollHandler = throttle(function() {
        const id = document.querySelector('.text-up')
        new TextUp( {id:id,text: keyCode.toUpperCase()} )
    }, 500);

    loadScenes();
    async function loadScenes(){
        gltf = await loadStaticScene();
        gltf_v11 = await loadStaticScene_v11();
        showLoading();
        handleIntoScene();
    }

    function loadStaticScene(){
        return new Promise(resolve => {
            let loader = new GLTFLoader();
            const url = "./models/gltf/WM161_zhankai_1k.glb"
            loader.load(url, (gltf) => {
                gltf.scene.position.set(0,27,0)
                gltf.scene.scale.set(15, 15, 15);
                gltf.scene.traverse(function (object){
                    object.castShadow = true
                    // if (object.isMesh){
                    // 设置成线性几何图，会卡顿
                    //     const edges = new THREE.EdgesGeometry(object.geometry);
                    //     const edgesMaterial = new THREE.LineBasicMaterial({
                    //         color: '#00ffff'
                    //     })
                    //     const line = new THREE.LineSegments(edges,edgesMaterial);
                    //     object.add(line);
                    //     object.material = new THREE.MeshLambertMaterial({
                    //         color: '#004444',
                    //         transparent: true,
                    //         opacity: 0.4
                    //     })
                    // }
                })
                resolve(gltf);
            },(event) =>{
                const {loaded,total} = event
                handleLoadProgress({url,loaded,total})
            });
        })
    }

    function loadStaticScene_v11(){
        return new Promise(resolve => {
            let loader = new GLTFLoader();
            const url = "./models/gltf/wm161_v11_zhedie_1k.glb"
            loader.load(url, (gltf) => {
                gltf.scene.position.set(0,-27,0)
                gltf.scene.scale.set(15, 15, 15);
                gltf.scene.traverse(function (object){
                    object.castShadow = true
                })
                resolve(gltf);
            },(event) =>{
                const {loaded,total} = event
                handleLoadProgress({url,loaded,total})
            });
        })
    }

    function handleLoadProgress({url, loaded, total}){
        const percentage = ((loaded / total) * 100).toFixed(2);
        if (/.*\.(blob|glb)$/i.test(url)) {
            updateLoadingProgress(`加载场景模型：${percentage}%`);
        }

    }

    function updateLoadingProgress(loading_text){
        progress && (progress.textContent = loading_text);
    }

    function showLoading() {
        loading_complete.classList.remove("display-none");
    }

    function handleIntoScene(){
        loading_con.classList.add("hide-loading");
        loading_con.addEventListener('animationend', function() {
            loading_con.classList.add("display-none")
            document.body.appendChild(tip)
            init();
            animate();
        });
    }


    function init() {
        const initColorMap = () =>{
            colorMap.forEach(item=> {
                const dom = document.createElement('div');
                dom.style.background = item;
                dom.classList.add('color-map-item');
                color_map.appendChild(dom);
            })
        }
        const initScene = () =>{
            scene = new THREE.Scene();
            scene.background = new THREE.Color( 0x333333 );
            scene.fog = new THREE.Fog( 0x333333, 4, 20 );
            // 添加雪碧模型
            const map = new THREE.TextureLoader().load( '../image/check.png' );
            const material = new THREE.SpriteMaterial( { map: map, color: 0xffffff } );
            sprite = new THREE.Sprite(material);
            sprite.name = 'sprite'
            sprite.scale.set(0.2,0.2,0.2)
            sprite.position.set(0,10,1)
            scene.add(sprite)
        }

        const initCamera = () =>{
            camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 100 );
            camera.position.set( 10, 20, 10 );

            cubeRenderTarget = new THREE.WebGLCubeRenderTarget(1024);
            cubeRenderTarget.texture.type = THREE.HalfFloatType;
            cubeCamera = new THREE.CubeCamera(0.1,200,cubeRenderTarget);

            // const gui = new GUI()
            // gui.add(camera.position, 'x', 0, 100)
            // gui.add(camera.position, 'y', 0, 100)
            // gui.add(camera.position, 'z', 0, 100)
        }

        const hemiLight = () =>{
            const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444, 3 );
            hemiLight.position.set( 0, 20, 0 );
            scene.add( hemiLight );
        }

        const initMash = () =>{
            const geometry = new THREE.BoxGeometry();
            const material = new THREE.MeshPhongMaterial( { color: '#8F8BFF' } );
            mesh = new THREE.Mesh( geometry, material );
            mesh.scale.set( 1.5, 1.5, 1.5 );
            mesh.castShadow = true;
            mesh.receiveShadow = true;
            mesh.position.y = 0.5;
            mesh.name = 'cube'
            scene.add( mesh );
        }

        const initGltfGroup = () =>{
            scene.add( gltf.scene );
            scene.add( gltf_v11.scene );
        }

        const initControls = () =>{
            controls = new OrbitControls( camera, canvas );
            controls.minDistance = 1.5
            // controls.enableDamping = true
            controls.maxDistance = 10
            controls.target.set(0,0.5,0)
        }

        const initLight = () =>{
            dLight = new THREE.DirectionalLight( 0xffffff, 3 );
            dLight.position.set( 0, 20, 10 );
            dLight.castShadow = true;
            dLight.shadow.camera.top = 2;
            dLight.shadow.camera.bottom = - 2;
            dLight.shadow.camera.left = - 2;
            dLight.shadow.camera.right = 2;
            scene.add( dLight );

        }

        const initGround = () =>{
            const planeGeometry = new THREE.PlaneGeometry( 40, 40 )
            const meshPhongMaterial = new THREE.MeshStandardMaterial( {
                color: 0x333333,
                depthWrite: false,
                roughness: 0.05,
                metalness: 0.5,
                envMap: cubeRenderTarget.texture,
            } )
            const ground = new THREE.Mesh( planeGeometry, meshPhongMaterial);
            ground.rotation.x = - Math.PI / 2;
            ground.receiveShadow = true;
            addShader( meshPhongMaterial );
            scene.add( ground );
        }
        const initGrid = () =>{
            grid = customGrid()
            scene.add( grid );
        }
        const initGrid_1 = () =>{
            grid_1 = customGrid();
            grid_1.position.z = 20;
            scene.add( grid_1 );

        }
        const customGrid = () =>{
            const grid = new THREE.GridHelper( 40, 20, 0xdddddd, 0xdddddd );
            grid.material.opacity = 0.2;
            grid.material.transparent = true;
            return grid
        }

        const initAxesHelper = () =>{
            let axesHelper = new THREE.AxesHelper( 3 );
            scene.add( axesHelper );
        }
        const initRaycaster = () =>{
            raycaster = new THREE.Raycaster();
            // raycaster.far = 3
        }


        const initRender = () =>{
            renderer = new THREE.WebGLRenderer( { antialias: true,canvas:canvas } );
            renderer.setPixelRatio( window.devicePixelRatio );
            renderer.setSize( window.innerWidth, window.innerHeight );
            renderer.shadowMap.enabled = true;

            // 后期处理
            composer = new EffectComposer( renderer );
            const renderPass = new RenderPass( scene, camera );
            composer.addPass( renderPass );
            outlinePass = new OutlinePass( new THREE.Vector2( window.innerWidth, window.innerHeight ), scene, camera );
            composer.addPass( outlinePass );

            // const textureLoader = new THREE.TextureLoader();
            // textureLoader.load( '../image/tri_pattern.jpg', function ( texture ) {
            //    用贴图方式设置高亮边
            //     outlinePass.patternTexture = texture;
            //     texture.wrapS = THREE.RepeatWrapping;
            //     texture.wrapT = THREE.RepeatWrapping;
            //
            // } );

            outlinePass.edgeStrength = 10.0 // 边框的亮度
            outlinePass.edgeGlow = 1 // 光晕[0,1]
            outlinePass.usePatternTexture = false // 是否使用父级的材质
            outlinePass.edgeThickness = 1.0 // 边框宽度
            outlinePass.downSampleRatio = 1 // 边框弯曲度
            outlinePass.pulsePeriod = 5 // 呼吸闪烁的速度
            outlinePass.visibleEdgeColor.set(parseInt(0x00ff00)) // 呼吸显示的颜色
            outlinePass.hiddenEdgeColor = new THREE.Color(0, 0, 0) // 呼吸消失的颜色
            outlinePass.clear = true

            const outputPass = new OutputPass();
            composer.addPass( outputPass );

            effectFXAA = new ShaderPass( FXAAShader );
            effectFXAA.uniforms[ 'resolution' ].value.set( 1 / window.innerWidth, 1 / window.innerHeight );
            effectFXAA.renderToScreen = true
            composer.addPass( effectFXAA );

        }
        initColorMap();
        initCamera();
        initScene();
        hemiLight();
        initGltfGroup();
        // initMash();
        initLight();
        initGround();
        initGrid();
        initGrid_1();

        initRender();
        initControls();
        // initAxesHelper();
        initRaycaster();
        initAddEventListener();
        initMobileControls();

        handleDockClick();

    }

    function initAddEventListener(){
        window.addEventListener( 'resize', onWindowResize );

        canvas.addEventListener('mousemove', function(event) {
            client = {
                x: event.clientX,
                y: event.clientY
            }
            clearMouse();
            mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
        });

        document.addEventListener('keydown', function(event) {
            keyCode = event.key === ' '? 'space' : event.key
            camera.position.addScaledVector(velocity, 0.5);
            mouse = new THREE.Vector2(0,0)

            switch (event.key) {
                case 'w':
                    handleMobileCamera(new THREE.Vector3(0,0,-1));
                    break;
                case 's':
                    handleMobileCamera(new THREE.Vector3(0,0,1));
                    break;
                case 'a':
                    handleMobileCamera(new THREE.Vector3(-1,0,0));
                    break;
                case 'd':
                    handleMobileCamera(new THREE.Vector3(1,0,0));
                    break;
                case ' ':
                    jump();
                    break;
            }
        });
        // 点击的时候实现一个切换相机效果
        // canvas.addEventListener('mousedown', onMousedown);
        //
        // canvas.addEventListener('mouseup', onMouseup);
        closeMask.addEventListener('click', handleCloseMask)
        imageMask.addEventListener("mousedown", handleImageMaskMouseDown);
        imageMask.addEventListener("mouseup", handleImageMaskMouseUp);

    }
    function initMobileControls(){
        if ('ontouchstart' in document.documentElement) {
            const stickContainer = document.createElement('div')
            stickContainer.style.cssText = `
                position: fixed;
                width: 80px;
                height: 80px;
                z-index: 99999999;
                border-radius: 50%;
                bottom: 110px;
                left: 50%;
                margin-left: -40px;
            `
            document.body.appendChild(stickContainer);
            new JoyStick(stickContainer, {}, (status) => {
                const position = new THREE.Vector2(status.x, status.y);
                const angle = position.angle();
                let radio = angle * (180 / Math.PI)

               const keyCodeMap = [
                   ['w',radio > 45 && radio <= 135,new THREE.Vector3(0,0,-1)],
                   ['a',radio> 135 && radio <= 225,new THREE.Vector3(-1,0,0)],
                   ['s',radio > 225 && radio <= 315,new THREE.Vector3(0,0,1)],
                   ['d',radio > 315 || radio <= 45,new THREE.Vector3(1,0,0)],
               ]
                for (let [_key,_value,_Vector] of keyCodeMap){
                    if(_value){
                        keyCode = _key
                        handleMobileCamera(_Vector);
                    }
                }
            })
        }
    }

    function handleMobileCamera(vector){

        const angle = controls.getAzimuthalAngle();
        const speed = 0.25
        temp_vector.set(vector.x,vector.y,vector.z).applyAxisAngle(up_vector, angle);
        camera.position.addScaledVector(temp_vector, speed);

        throttledScrollHandler();
    }

    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();

        renderer.setSize( window.innerWidth, window.innerHeight );
        composer.setSize( window.innerWidth, window.innerHeight );

        effectFXAA.uniforms[ 'resolution' ].value.set( 1 / window.innerWidth, 1 / window.innerHeight );
    }

    function onMousedown() {
        isDown = true;
        timer = setInterval(mouseCamera, 1000/60);
    }

    function onMouseup() {
        clearMouse();
    }

    function handleCloseMask(){
        photoMask.classList.add('display-none');
        imageMask.src = ''
    }

    function handleImageMaskMouseDown(event) {
        if (event.button === 0) {
            longPressTimer = setTimeout(handleLongPress, 1000);
        }
    }

    function handleImageMaskMouseUp() {
        clearTimeout(longPressTimer);
    }

   async function handleLongPress() {
       const dataURL = await getGltfUrl();
        const link = document.createElement("a");
        link.href = dataURL.toString();
        link.download = "canvas_image.png";
        link.click();
    }

    function animate(){
        requestAnimationFrame( animate );
        cubeCamera.position.copy(camera.position);
        cubeCamera.position.y *= -1;
        cubeCamera.update(renderer,scene);

        updateJump();
        controls.update();
        TWEEN.update();
        beginFly && flyGltf();
        isLoading && updateCamera();
        !isLoading && hoverPoint();
        // renderer.render( scene, camera );
        composer.render();

        if (shaders?.length) {
            // 着色器动画
            shaders.forEach((shader) => {
                shader.uniforms.uTime.value += 0.008;
                if (shader.uniforms.uTime.value >= 1) {
                    shader.uniforms.uTime.value = 0;
                }
            });
        }
    }

    function flyGltf(){
        gltf.scene.traverse((e)=>{
            const polySurface_number = e.name.replace(/polySurface/g,'')
            const rotatePoly = ['60','81','90','94']
            if (rotatePoly.includes(polySurface_number)){
                e.rotation.y += flySeep
            }
        })
        if (flySeep <= 1 && isFly){
            flySeep += 0.002
        }else {
            flySeep -= 0.002
        }
        if (flySeep <= 0){
            flySeep = 0
        }
        grid.position.z -= flySeep
        grid_1.position.z -= flySeep
        if (grid.position.z <= -20){
            grid.position.z = 0
        }
        if (grid_1.position.z <= 0){
            grid_1.position.z = 20
        }
        // gltf.scene.rotation.x = 0.1
    }

    function cleanFly(){
        if (!isCheck){
            beginFly = false;
            isFly = false;
            flySeep = 0
        }
    }

    function handleDockClick() {
        const handleCheck = () =>{
            isCheck = !isCheck;
            cleanFly();
            checkGltf();
        }

        const checkGltf = () =>{
            const gltf_vector = isCheck ? new THREE.Vector3(0,0,0): new THREE.Vector3(0,27,0)
            const sprite_vector = isCheck ? new THREE.Vector3(0,1.5,1): new THREE.Vector3(0,10,1)
            const gltf_v11_vector = isCheck ? new THREE.Vector3(0,0,0): new THREE.Vector3(0,-27,0)
            new TWEEN.Tween( gltf.scene.position)
                .to({ x: gltf_vector.x, y: gltf_vector.y, z: gltf_vector.z }, 500)
                .easing(TWEEN.Easing.Quadratic.Out)
                .start();
            new TWEEN.Tween( gltf_v11.scene.position)
                .to({ x: gltf_v11_vector.x, y: gltf_v11_vector.y, z: gltf_v11_vector.z }, 500)
                .easing(TWEEN.Easing.Quadratic.Out)
                .start();
            new TWEEN.Tween( sprite.position)
                .to({ x: sprite_vector.x, y: sprite_vector.y, z: sprite_vector.z }, 500)
                .easing(TWEEN.Easing.Quadratic.Out)
                .start();
        }

        const handleColor = () =>{
            isAppendColor = !isAppendColor;
            if (isAppendColor){
                color_map.classList.add('show-color-map');
                delegate(color_map, 'click', 'div', function(e){
                    setColorMap(e.target.style.backgroundColor)
                })
            }else {
                color_map.classList.remove('show-color-map');
            }

            const setColorMap = (color) =>{
                gltf.scene.traverse((e)=>{
                    const polySurface_number = e.name.replace(/polySurface/g,'')
                    const windBlade = ['21','101','201']
                    if (windBlade.includes(polySurface_number)){
                        e.material.color.set(color);
                        isCheck = true;
                        checkGltf();
                    }
                })
            }

        }

        const handleFly = () =>{
            beginFly = true
            isCheck = true
            isFly =  !isFly
            checkGltf();
        }

        const handleTakePicture = async ()=> {
            imageMask.src = await getGltfUrl();
            photoMask.classList.remove('display-none');
            showWhiteScreen()
        }

        delegate(dock, 'click', 'i', function(e){
            const DOCK_MAP = [
                {
                    target: ()=> e.target.classList.contains('icon_check'),
                    handler: handleCheck
                },
                {
                    target: ()=> e.target.classList.contains('icon_color'),
                    handler: handleColor
                },
                {
                    target: ()=> e.target.classList.contains('icon_fly'),
                    handler: handleFly
                },
                {
                    target: ()=> e.target.classList.contains('icon_photo'),
                    handler: handleTakePicture
                }
            ]
            const event = DOCK_MAP.find(item => item.target());
            if (event) {
                event.handler();
            }
        })

    }

    function getGltfUrl(){
        return new Promise((resolve)=>{
            renderer.render( scene, camera );
            resolve(canvas.toDataURL('image/png'))
        })
    }

    function showWhiteScreen(){
        const screen = document.createElement('div');
        screen.style.cssText = `
            background: rgba(255,255,255,1);
            position: fixed;
            z-index: 9999;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            animation: fade 0.5s cubic-bezier(0.075,0.82,0.165,1) forwards;
            `
        document.body.appendChild(screen);
        setTimeout(function() {
            document.body.removeChild(screen);
        }, 100);
    }

    function updateJump(){
        if (isJumping) {
            camera.position.y += jumpVelocity;
            jumpVelocity -= 0.05;
            if (camera.position.y <= 1.5) {
                camera.position.y = 1.5;
                isJumping = false;
            }
        }
    }


    function jump() {
        if (!isJumping) {
            jumpVelocity = 0.3 * maxJumpHeight
            isJumping = true;
        }
    }

    function updateCamera() {
        const vector = new THREE.Vector3()
        targetPosition.addScaledVector(vector, 0.25);
        const {x,y,z} = targetPosition

        camera.position.x += (targetPosition.x - camera.position.x) * dampingFactor;
        camera.position.y += (targetPosition.y - camera.position.y) * dampingFactor;
        camera.position.z += (targetPosition.z - camera.position.z) * dampingFactor;
        const cameraX = camera.position.x.toFixed(1);
        const cameraY = camera.position.y.toFixed(1);
        const cameraZ = camera.position.z.toFixed(1);
        if (Number(cameraX) <= x && Number(cameraY) <= y && Number(cameraZ) <= z){
            isLoading = false
            title.classList.remove("display-none");
            dock_con.classList.remove("display-none");
        }

    }

    function mouseCamera() {
        const vector = new THREE.Vector3();
        endPosition.addScaledVector(vector, 0.25);
        const {x,y,z} = endPosition

        camera.position.x += (endPosition.x - camera.position.x) * dampingFactor;
        camera.position.y += (endPosition.y - camera.position.y) * dampingFactor;
        camera.position.z += (endPosition.z - camera.position.z) * dampingFactor;
        const cameraX = camera.position.x.toFixed(1);
        const cameraY = camera.position.y.toFixed(1);
        const cameraZ = camera.position.z.toFixed(1);
        if (Number(cameraX) > x && Number(cameraY) > y && Number(cameraZ) > z){
            clearMouse()
        }

    }

    function clearMouse(){
        isDown = false
        clearInterval(timer);
    }

    function spriteClick(){
        raycaster.setFromCamera(mouse, camera);
        let intersects = raycaster.intersectObject(sprite);
        if (intersects.length && intersects[0].object.name){
            shaders.forEach((shader) => {
                shader.uniforms.uColor = { value: new THREE.Color('#8F8BFF') };
            });
        }
    }

    canvas.addEventListener('click',spriteClick)

    function hoverPoint(){
        if (mouse.x === 0 && mouse.y ===0) return;
        raycaster.setFromCamera(mouse, camera);
        let intersects = raycaster.intersectObjects(scene.children);
        if(INTERSECTED) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
        if (intersects.length && intersects[0].object.name && intersects[0].object.name !== 'sprite'){
            INTERSECTED = intersects[0].object;
            addSelectedObject( INTERSECTED );
            outlinePass.selectedObjects = selectedObjects;
            // INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
            // INTERSECTED.material.emissive.setHex( 0x8F8BFF );
            showTip(INTERSECTED?.material.name)
        }else {
            // if (INTERSECTED) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
            INTERSECTED = null;
            outlinePass.selectedObjects = []
            tip.style.display = 'none';
        }
    }

    function addSelectedObject( object ) {
        selectedObjects = [];
        selectedObjects.push( object );
    }

    function showTip(title){
        tip.classList.add('tip');
        tip.textContent = title;
        tip.style.left = (client.x + 15) + 'px';
        tip.style.top = (client.y + 15) + 'px';
        tip.style.display = 'block';
    }

    function addShader(material){
        const vertexShader1 = `uniform float uSize;
              varying vec2 vUv;
              void main() {`;
        const vertexShader3 = `#include <fog_vertex>
                        vUv=vec2(-position.x,-position.y)/uSize;`;
        const fragmentShader1 = `varying vec2 vUv;
                uniform float uTime;
                uniform vec3 uColor;
                uniform float uSize;
                void main() {`;
        const fragmentShader2 = `#include <dithering_fragment>
            float d=length(vUv);
                  if(d >= uTime&&d<=uTime+ 0.1) {
                    gl_FragColor.rgb = gl_FragColor.rgb+mix(uColor,gl_FragColor.rgb,1.0-(d-uTime)*10.0 )*0.5  ;
                  }`;
        material.onBeforeCompile = (shader) => {
            shaders.push(shader);
            shader.uniforms.uSize = { value: 50 };
            shader.uniforms.uTime = { value: 0.1 };
            shader.uniforms.uColor = { value: new THREE.Color('#4ff9ff') };
            shader.vertexShader = shader.vertexShader.replace('void main() {', vertexShader1);
            shader.vertexShader = shader.vertexShader.replace(
                '#include <fog_vertex>',
                vertexShader3
            );
            shader.fragmentShader = shader.fragmentShader.replace('void main() {', fragmentShader1);
            shader.fragmentShader = shader.fragmentShader.replace(
                '#include <dithering_fragment>',
                fragmentShader2
            );
        };
    }

</script>
</html>
